using System;
using System.Reflection;

namespace ValidationFramework.Reflection
{
    /// <summary>
    /// A light-weight wrapper for <see cref="PropertyInfo"/>.
    /// </summary>
#if (!SILVERLIGHT)
    [Serializable]
#endif
    public class PropertyDescriptor : InfoDescriptor
    {
        // --- Fields

#if(WindowsCE || SILVERLIGHT)
		private readonly MethodInfo getMethodInfo;
#else
        private readonly FastInvokeHandler invoker;
#endif


        // --- Constructors

        /// <summary>
        /// For testing purposes
        /// </summary>
        /// <exclude/>
        protected PropertyDescriptor(RuntimeTypeHandle runtimeTypeHandle, string name)
            : base(runtimeTypeHandle, name)
        {
        }


        /// <summary>
        /// Initialize a new instance of <see cref="PropertyDescriptor"/>.
        /// </summary>
        /// <param name="propertyInfo">The <see cref="PropertyInfo"/> to wrap.</param>
        /// <param name="typeDescriptor">The <see cref="Reflection.TypeDescriptor"/> this <see cref="PropertyDescriptor"/> belongs to.</param>
        /// <param name="getMethodInfo">The get <see cref="MethodInfo"/> for the <see cref="PropertyInfo"/> being wrapped.</param>
        /// <exception cref="NullReferenceException"><paramref name="propertyInfo"/> is null.</exception>
        internal PropertyDescriptor(TypeDescriptor typeDescriptor, PropertyInfo propertyInfo, MethodInfo getMethodInfo)
            : base(getMethodInfo.ReturnType.TypeHandle, propertyInfo.Name)
        {
            if (propertyInfo.PropertyType.IsGenericParameter)
            {
                throw new ArgumentException("propertyInfo cannot be a genetic type.", "propertyInfo");
            }
            TypeDescriptor = typeDescriptor;
            IsStatic = getMethodInfo.IsStatic;
            IsVirtual = getMethodInfo.IsVirtual;
#if(WindowsCE || SILVERLIGHT)
            this.getMethodInfo = getMethodInfo;
#else
            invoker = MethodInvokerCreator.GetMethodInvoker(getMethodInfo);
#endif
			this.StateRules = new RuleCollection<PropertyStateRule>();
			this.ValueRules = new RuleCollection<PropertyValueRule>();
            AddStateRulesFromAttributes(propertyInfo);
            AddValueRulesFromAttributes(propertyInfo);
        }


        /// <summary>
        /// Gets a value indicating whether the property is virtual.
        /// </summary>
        public bool IsVirtual { get; private set; }

        internal void AddStateRulesFromAttributes(PropertyInfo propertyInfo)
        {
            var attributes = (PropertyStateRuleAttributeBase[])propertyInfo.GetCustomAttributes(typeof(PropertyStateRuleAttributeBase), true);
            foreach (var atrribute in attributes)
            {
                StateRules.Add(RuleFactory.CreatePropertyStateRule(propertyInfo, atrribute), atrribute.RuleSet);
            }
        }
        internal void AddValueRulesFromAttributes(PropertyInfo propertyInfo)
        {
            var attributes = (PropertyValueRuleAttributeBase[])propertyInfo.GetCustomAttributes(typeof(PropertyValueRuleAttributeBase), true);
            foreach (var atrribute in attributes)
            {
                ValueRules.Add(RuleFactory.CreatePropertyValueRule(propertyInfo, atrribute), atrribute.RuleSet);
            }
        }



        // --- Properties

        /// <summary>
        /// Gets a value indicating whether the <see cref="PropertyDescriptor"/> is static. 
        /// </summary>
        public bool IsStatic
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the <see cref="Reflection.TypeDescriptor"/> for this <see cref="PropertyDescriptor"/>.
        /// </summary>
        public TypeDescriptor TypeDescriptor
        {
            get;
            private set;
        }

        /// <summary>
        /// RuleCollection containing the ValueRules for the property this describes
        /// </summary>
        public RuleCollection<PropertyValueRule> ValueRules { get; private set; }

        /// <summary>
        /// RuleCollection containing the StateRules for the property this describes
        /// </summary>
        public RuleCollection<PropertyStateRule> StateRules { get; private set; }

        public int RuleCount
        {
            get
            {
                return this.ValueRules.Count + this.StateRules.Count;
            }
        }


        #region Methods

        /// <inheritdoc />
        public override object GetValue(object target)
        {
#if(WindowsCE || SILVERLIGHT)
			return getMethodInfo.Invoke(target, null);
#else
            return invoker(target);
#endif
        }

        #endregion
    }
}